package org.sanguine.tests.protei.springapp.mvc;

import org.sanguine.tests.protei.springapp.mvc.models.ErrorInfo;
import org.sanguine.tests.protei.springapp.mvc.models.Goods;
import org.sanguine.tests.protei.springapp.mvc.services.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;

@Controller
@RequestMapping("/api/v1/goods")
public class GoodsController {

    private static Logger log = Logger.getLogger(GoodsController.class.getName());

    @Autowired
    private Service<Goods> service;

    // Exception handlers section

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(RESTException.class)
    @ResponseBody
    public ErrorInfo handleInitFailure(HttpServletRequest req, RESTException ex) {
        log.severe(ex.getMessage());
        return new ErrorInfo(req.getRequestURI(), ex);
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler({SQLException.class, DataAccessException.class})
    @ResponseBody
    public ErrorInfo handleDatabaseException(HttpServletRequest req, Exception ex) {
        log.severe(ex.getMessage());
        return new ErrorInfo(req.getRequestURI(), ex);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({NotValidEntityException.class})
    @ResponseBody
    public ErrorInfo handleDatabaseException(HttpServletRequest req, RESTException ex) {
        log.severe(ex.getMessage());
        return new ErrorInfo(req.getRequestURI(), ex);
    }

    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler({NotFoundException.class, DeleteFailureException.class})
    @ResponseBody ErrorInfo handleNotFoundException(HttpServletRequest req, RESTException ex) {
        log.info(ex.getMessage());
        return new ErrorInfo(req.getRequestURI(), ex);
    }
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    @ExceptionHandler({CreationFailureException.class, UpdateFailureException.class})
    @ResponseBody ErrorInfo handleCreationFailureException(HttpServletRequest req, RESTException ex) {
        log.warning(ex.getMessage());
        return new ErrorInfo(req.getRequestURI(), ex);
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ErrorInfo handleOtherException(HttpServletRequest req, Exception ex){
        log.info(ex.getMessage());
        return new ErrorInfo(req.getRequestURI(), ex);
    }

    // REST handlers sections

    @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Goods>> getGoods(@RequestParam(value = "page", required = false, defaultValue = "0") int page,
                                  @RequestParam(value = "length", required = false, defaultValue = "100") int length, HttpServletRequest req, HttpServletResponse res) throws RESTException {
        log.info("GET All goods request " + " page=" + page + " length=" + length);
        if (service == null) throw new RESTException("Initialization failed");
        List<Goods> goods = new ArrayList<Goods>(length);
        log.info(goods.toString() + " size=" + goods.size());
        log.fine(printHeaders(req));
        boolean partial = service.getAll(page, length, goods);
        if (partial) return new ResponseEntity<List<Goods>>(goods, HttpStatus.PARTIAL_CONTENT);
        return new ResponseEntity<List<Goods>>(goods, HttpStatus.FOUND);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.FOUND)
    @ResponseBody
    public Goods getGoods(@PathVariable (value = "id") String id, HttpServletRequest req, HttpServletResponse res) throws RESTException {
        log.info("GET request for id " + id);
        if (service == null) throw new RESTException("Initialization failed");
        Goods goods = service.getById(id);
        log.info("Goods: " + goods);
        log.fine(printHeaders(req));
        if (goods == null) throw new NotFoundException("Not found any goods for id=" + id);
        return goods;
	}

    @RequestMapping(method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.CREATED)
    @ResponseBody
    public Goods createGoods(@RequestBody(required = true) Goods newGoods) throws RESTException {
        log.info("PUT request for goods: " + newGoods);
        if (service == null) throw new RESTException("Initialization failed");
        Goods goods = service.create(newGoods);
        if (goods == null) throw new CreationFailureException("Can't addEntity goods: " + newGoods);
        return goods;
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.OK)
    public void deleteEntityById(@PathVariable(value = "id") String id) throws RESTException {
        log.info("DELETE request for id " + id);
        if (service == null) throw new RESTException("Initialization failed");
        boolean success = service.deleteById(id);
        if (!success) throw new DeleteFailureException("Can't removeEntity goods for id=" + id);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public void updateEntityById(@PathVariable(value = "id") String id, @RequestBody(required = true) Goods goods) throws RESTException {
        log.info("POST request for id " + id + ", goods: " + goods);
        if (service == null) throw new RESTException("Initialization failed");
        boolean success = service.updateById(id, goods);
        if (!success) throw new UpdateFailureException("Can't updateEntity goods for id=" + id);
    }


    private String printHeaders(HttpServletRequest req) {
        StringBuilder stringBuilder = new StringBuilder();
        Enumeration headers = req.getHeaderNames();
        while (headers.hasMoreElements ()) {
            String name = (String)headers.nextElement();
            stringBuilder.append(name);
            stringBuilder.append(": ");
            stringBuilder.append(req.getHeader(name));
            stringBuilder.append("\n");
        }
        return stringBuilder.toString();
    }
}